function mdl = slcdiaggauss(mu, sigma, cachelevel)
%SLCDIAGGAUSS Constructs a Gaussian model with diagonal covariance matrix
%
% [ Syntax ]
%   - mdl = slcdiaggauss()
%   - mdl = slcdiaggauss(mu, sigma)
%   - mdl = slcdiaggauss(mu, sigma, cachelevel)
%
% [ Arguments ]
%   - mu:       The mean vector(s)
%   - sigma:    The variances
%   - mdl:      The constructed model object(s)
%
% [ Description ]
%   - mdl = slcdiaggauss() constructs an empty Gaussian model.
%
%     This object typically serves as the basis for following estimation.
%     A conventional way to estimate a model is
%       \{
%           mdl = estimate(slcfullcov(), X, ...)
%       \}
%     Please refer to the help of estimate method for details.
%
%   - mdl = slcdiaggauss(mu, sigma) constructs a Gaussian model with
%     given mean vector and covariance matrix.
%
%     To construct a single model, mu should be a d x 1 vector, while
%     sigma can be given in either of the following forms:
%       - d x 1 vector to specify the diagonal entries of the covariance
%       - a scalar to specify an isotropic covariance matrix with the
%         given variance
%
%     Note that you can also construct a set of Gaussian models as 
%     follows. Suppose you are to make K models, then you should input 
%     mu as a d x K matrix with each column giving a mean vector. 
%     And the sigma can be given in either of the following forms:
%       - d x K matrix, with each column giving the diagonal entries of
%         the covariance
%       - {d x 1 matrix}, with the vector in the cell giving the 
%         diagonal entries of a covariance shared by all models
%       - 1 x K matrix, with each element giving the isotropic variance
%       - {1 x 1 scalar} with the scalar in the cell giving the
%         isotropic variance shared by all models.
%
%   - mdl = slcdiagauss(mu, sigma, cachelevel). With this syntax, you can 
%     control the level of pre-computation caching. There are four levels:
%       \{:
%           - 0:        cache nothing
%           - 1:        cache useful scalars,
%                       including log(det(cov)), trace(cov), and 
%                       mu^T cov^{-1} mu
%           - 2:        cache useful vectors in addition to the above
%                       including diagonal elements of the inv(cov),
%                       and cov^{-1} mu
%       \:}
%     By default, the cache level is 2.
%
% [ History ]
%   - Created by Dahua Lin, on Dec 24, 2007
%

%% main

if nargin == 0     % construct empty model
    mu = [];
    sigma = [];
    cachelevel = 0;
    
    cov_shared = false;
    
else     % construct model with parameters
    
    if nargin < 2
        error('sltoolbox:slcdiaggauss:slcdiaggauss:invalidarg', ...
            'The sigma should be given.');
    end

    if nargin < 3
        cachelevel = 2;
    end
    
    % mu
    
    assert(isnumeric(mu) && ndims(mu) == 2, ...
        'sltoolbox:slcdiaggauss:slcdiaggauss:invalidarg', ...
        'mu should be a numeric matrix.');    
    [d, K] = size(mu);
    
    if K > 1
        mu = slslice(mu, 1);
    end
    
    % sigma
    
    if isnumeric(sigma)   % non-share
        assert(ndims(sigma) == 2, ...
            'sltoolbox:slcdiaggauss:slcdiaggauss:invalidarg', ...
            'sigma should be a 2D matrix.');
    
        [d1, d2] = size(sigma);
        
        if d1 == d && d2 == K
            sigma = slslice(sigma, 1);
            
        elseif d1 == 1 && d2 == K
            sigma = arrayfun(@(x) ones(d, 1) * x, sigma', ...
                'UniformOutput', false);
            
        else
            error('sltoolbox:slcdiaggauss:slcdiaggauss:invalidarg', ...
                'The size of sigma is invalid.');
        end
        
        cov_shared = false;
        
        
    elseif iscell(sigma) && numel(sigma) == 1  % share
        
        sigma = sigma{1};
        assert(ndims(sigma) == 2 && ...
            (isscalar(sigma) || isequal(size(sigma), [d, 1])), ...
            'sltoolbox:slcdiaggauss:slcdiaggauss:invalidarg', ...
            'The size of sigma is invalid');
        
        if isscalar(sigma)
            sigma = sigma * ones(d, 1);
        end
        
        cov_shared = true;
                                    
    else
        error('sltoolbox:slcdiaggauss:slcdiaggauss:invalidarg', ...
            'The specified sigma is invalid.');
    end
    
end

% make struct

mdl = class(struct(...
    'mu', mu, ...
    'sigma', sigma, ...
    'clevel', 0, ...        % cache level
    'ldsm', [], ...         % log(det(cov))
    'trsm', [], ...         % trace(cov)
    'invsm', [], ...        % inv(cov)
    'ism_mu', [], ...       % inv(cov) * mu
    'mu_ism_mu', [] ...     % mu^T * inv(cov) * mu
    ), ...
    'slcdiaggauss');

if cachelevel > 0
    mdl = cache(mdl, cachelevel, cov_shared);
end
    
